package com.itheima.reggie.aop;


import com.itheima.reggie.annotations.ItheimaCacheEvict;
import com.itheima.reggie.annotations.ItheimaCacheable;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
public class CacheAdvice {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 实现 @ItheimaCacheable的功能：只要是有 @ItheimaCacheable 的方法，我就会进行增强
     */
    @Around(value = "@annotation(cache)")
    public Object addCache(ProceedingJoinPoint pjp, ItheimaCacheable cache) throws Throwable {

        Object key = getKey(pjp, cache.value(),cache.key());


        Object result = redisTemplate.opsForValue().get(key);
        if(result == null){//缓存中没有
            // 如果缓存查询不到，则执行目标对象的方法
            result = pjp.proceed();
            //存入缓存
            int ttl = cache.ttl();
            if(ttl>=0){
                redisTemplate.opsForValue().set(key,result,ttl, TimeUnit.MINUTES);
            }else {
                redisTemplate.opsForValue().set(key,result);
            }
        }
        // 将目标方法返回的结果存入缓存中
        return result;
    }

    /**
     * 实现 @ItheimaCacheEvict：只要是有 @ItheimaCacheEvict 的方法，我就会进行增强
     */

    @Before(value = "@annotation(evict)")
    public void clearCache(JoinPoint jp, ItheimaCacheEvict evict){
        boolean clearAll = evict.allEntries();//是否清除所有缓存
        String value = evict.value();
        if(clearAll){
            Set keys = redisTemplate.keys(value+":*");
            for (Object key : keys) {
                redisTemplate.delete(key);
            }
        }else {
            Object key = getKey(jp, evict.value(), evict.key());
            redisTemplate.delete(key);
        }
    }


    private Object getKey(JoinPoint pjp,String keyPreffix,String keyExpression) {
        // 解析 Spring EL 表达式
        ExpressionParser parser = new SpelExpressionParser();//SPEL解析器

        EvaluationContext context = new StandardEvaluationContext();//模型数据
        // id  12212  context.setVariable("id","12212");
        // user  user对象   context.setVariable("user",user);
        // 如果我知道当前执行方法的 参数信息： 参数名称 name 和  对应的值 value
        Object[] values = pjp.getArgs();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        String[] parameterNames = signature.getParameterNames();
        if(parameterNames!=null && parameterNames.length>0){
            for (int i = 0; i < parameterNames.length; i++) {
                context.setVariable(parameterNames[i],values[i]);
            }
        }
        Expression expression = parser.parseExpression(keyExpression);
        Object key = expression.getValue(context);
        key = keyPreffix+":"+key;
        return key;
    }


}
